#ifndef __brick_kenny_dkfjiwoo3w3__
#define __brick_kenny_dkfjiwoo3w3__

#include <dlfcn.h>

#include <map>
#include <string>
#include <memory>
#include <functional>

#include <hude/aio/context.hpp>

#include "app/settings.hpp"

#ifdef __cplusplus
#define EXTERN_C extern "C"
#endif //__cplusplus

namespace ha
{
    using json_t     = hude::utils::json_t;
    using ctx_t      = hude::aio::context_t<>;
    using aio_t      = hude::aio::aio_t<>;
    using evt_type_t = ctx_t::loop_t::evt_t;

    /*********************************************************************
     * @brief 消息标识类型
     */
    using mid_t = uint32_t;

    /*********************************************************************
     * @brief 组合消息ID类型
     */
    union _mid_t
    {
        uintptr_t id;

        struct
        {
            uint32_t group;
            uint32_t element;
        };
    };

    inline bool operator==( _mid_t a, _mid_t b ) { return a.id == b.id; }
    inline bool operator<( _mid_t a, _mid_t b ) { return a.id < b.id; }

    /*********************************************************************
     * @brief 积木接口定义
     */
    struct brick_t
    {
        using cb_recv_t = std::function< void( const hude::buffer_t& ) >;

        cb_recv_t cb_recv_;

        virtual ~brick_t() {}

        void cb_recv( cb_recv_t cb = cb_recv_t{} ) { cb_recv_ = cb; }

        virtual int send( const void*, size_t ) = 0;

        virtual void close() = 0;

        virtual bool settings( const json_t& args ) = 0;

        virtual void loglevel( hude::log_level_t level ) { hude::log_debug_info_t::level( level ); }
    };

    /*********************************************************************
     * @brief 积木指针
     */
    using pbrick_t = std::shared_ptr< brick_t >;

    /*********************************************************************
     * @brief 积木抽象工厂
     */
    class factory_brick_t
    {
    public:
        /*********************************************************************
         * @brief 积木工厂
         */
        using factory_t = std::function< pbrick_t( aio_t, const json_t& ) >;

        /*********************************************************************
         * @brief 工厂队列
         */
        using map_factory_t = std::map< std::string, factory_t >;

        /*********************************************************************
         * @brief 插件类型声明
         */
        struct plug_t;

    public:
        /*********************************************************************
         * @brief 初始化
         */
        bool init( const json_t& );

        /*********************************************************************
         * @brief 抽象生产, 如果找到具体的工厂则生产实例，否则返回空对象
         */
        pbrick_t product( aio_t aio, const std::string& name, const json_t& args )
        {
            map_factory_t::iterator itr = factorys_.find( name );

            return ( itr == factorys_.end() )? pbrick_t( nullptr ) : itr->second( aio, args );
        }

        /*********************************************************************
         * @brief 单例入口
         */
        static factory_brick_t* single();

    private:
        //禁止外部实例化
        // {{
        factory_brick_t() {}
        factory_brick_t( const factory_brick_t& ) = delete;
        factory_brick_t( factory_brick_t&& ) = delete;
        factory_brick_t& operator=( const factory_brick_t& );
        // }}

        factory_t plug( const std::string& path );

    private:
        map_factory_t factorys_;

    };

    /*********************************************************************
     * @brief 插件类型定义
     */
    struct factory_brick_t::plug_t
    {
        typedef pbrick_t ( *pfactory_t )( aio_t, const json_t& );
        typedef std::tuple< void*, pfactory_t > handle_t;

        std::shared_ptr< handle_t > handle_;

        /*********************************************************************
         * @brief 载入插件
         */
        plug_t( const std::string& path ) :
            handle_(
                new handle_t{ ::dlopen( path.c_str(), RTLD_NOW ), nullptr },
                []( handle_t* p ){ auto h = std::get<0>(*p); if( h ) ::dlclose( h ); delete p; }
            )
        {
            auto& h = std::get<0>( *handle_ );
            auto& s = std::get<1>( *handle_ );

            s = ( h == nullptr )? nullptr : (pfactory_t)(::dlsym( h, "_factory_brick" ));
        }

        /*********************************************************************
         * @brief 载入插件
         */
        plug_t( const plug_t& ins ) : handle_( ins.handle_ ) {}

        /*********************************************************************
         * @brief 载入插件
         */
        plug_t& operator=( const plug_t& ins )
        {
            if ( &ins != this )
            {
                handle_ = ins.handle_;
            }

            return *this;
        }

        /*********************************************************************
         * @brief ...
         */
        void* handle() const { return std::get<0>( *handle_ ); }

        /*********************************************************************
         * @brief ...
         */
        pfactory_t symc() const { return std::get<1>( *handle_ ); }

        /*********************************************************************
         * @brief 从插件产生积木
         */
        operator bool() const { return symc() != nullptr; }

        /*********************************************************************
         * @brief 从插件产生积木
         */
        pbrick_t operator()( aio_t aio, const json_t& cfg )
        {
            auto s = symc();
            return s? (*s)( aio, cfg ) : pbrick_t{};
        }
    };

    /*********************************************************************
     * @brief 针对需要基于异步IO操作实现的积木所提供的公共基类
     */
    template< typename _subclass >
    class _brick_aio_t :
        public brick_t
    {
    public:
        using handle_t = int;

    public:
        _brick_aio_t( aio_t aio ) : aio_( aio ) {}

        int recv( void*, size_t );

    protected:
        /*********************************************************************
         * @brief 异步IO上下文
         */
        aio_t aio_;

        /*********************************************************************
         * @brief 设置文件描述符为非阻塞
         */
        static int set_noblock( int handle )
        {
            int flags = 0;

            if ( -1 == ( flags = fcntl( handle, F_GETFL ) ) )
            {
                return -1;
            }

            if ( -1 == fcntl( handle, F_SETFL, flags | O_NONBLOCK ) )
            {
                return -1;
            }

            return 0;
        }

        /*********************************************************************
         * @brief 异步IO事件监听
         */
        int mount( handle_t handle )
        {
            auto on_aio = [=]( uint32_t evts, hude::any_t& )
            {
                // llog_dug( "<brick>", "\t Event of aio: <evts: %#x>", evts );

                if( 0U != ( evts & evt_type_t::emEPOLLIN ) )
                {
                    while( this->cb_recv_ )
                    {
                        hude::buffer_t buffer( 1024U );

                        auto ret = ((_subclass*)this)->recv( &(buffer[0]), buffer.size() );

                        if( ret > 0 )
                        {
                            buffer.resize( ret );

                            // llog_dug( "<brick>", "\t\t Recv buffer[ %s ]", hude::utils::to_hex( buffer ).c_str() );

                            this->cb_recv_( buffer );
                        } else {
                            // if( ret == 0 ) llog_war( "<brick>", "\t Event of aio: recv() == %d.", ret );

                            break;
                        }
                    }
                }

                if( 0U != ( evts & evt_type_t::emEPOLLHUP ) )
                {
                    llog_nte( "<brick>", "\t Remote disconnected." );
                }

                if( 0U != ( evts & evt_type_t::emEPOLLERR ) )
                {
                    llog_war( "<brick>", "\t Error event." );

                    this->close();
                }
            };

            return aio_->evt_commit( handle, on_aio );
        }

        /*********************************************************************
         * @brief 取消异步IO事件监听
         */
        void unmount( handle_t handle )
        {
            aio_->evt_remove( handle );
        }

    };

}  // namespace ha

#endif  //__brick_kenny_dkfjiwoo3w3__
